# ICEMGenerator - 高效的ICEM CFD网格划分Replay Script生成工具
# 模块： 生成网格模块，可通过(1)由Block生成网格;(2)直接创建四面体网格 两种方式生成网格
# 作者： weixc
# 邮箱： 12028045 [at] zju.edu.cn
# 项目主页： https://gitee.com/weixinchong/icemgenerator

import os
from .curve import Curve
from .surface import Surface
from .body import Body
from .part import Part
from .block import Block


class TetraMesh():
    '''四面体网格'''

    applied = False
    mode = 'all_geo'
    from_part = None
    script = ''
    periodicity_script = ''

    def create(from_part: Part = None):
        '''创建四面体网格
        :param from_part: 从哪个Part开始创建网格，未给定时，从整个几何体创建网格'''
        TetraMesh.applied = True
        if from_part is None:
            TetraMesh.script = TetraMesh.gen_from_geo()
        else:
            TetraMesh.script = TetraMesh.gen_from_part(from_part)

    def gen_from_geo() -> str:
        '''生成从整个几何体创建四面体网格的Replay Script
        :return: 生成的Replay Script'''
        parts_expr = 'GEOM SHEET ORFN ' + ' '.join([str(b) for b in Body.final_members] + [str(p) for p in Part.members])
        text = 'ic_save_tetin temp_tetra.tin\n' \
            + 'ic_run_tetra temp_tetra.tin ./tetra_mesh.uns run_cutter 1 delete_auto 1 run_smoother 0 fix_holes 1 n_processors 1 in_process 1 log ./tetra_cmd.log\n' \
            + 'ic_geo_set_modified 1\n' \
            + 'ic_uns_update_family_type visible {%s} {!LINE_2 TRI_3 !TETRA_4} update 0\n' % parts_expr \
            + 'ic_boco_solver\n' \
            + 'ic_boco_clear_icons\n' \
            + 'ic_uns_diagnostic diag_type single quiet 1\n' \
            + 'ic_smooth_elements map all upto 0.4 iterations 5 fix_families {} n_processors 1 smooth TRI_3 float TETRA_4 laplace 1\n' \
            + 'ic_smooth_elements map all upto 0.4 iterations 5 prism_warp_weight 0.5 fix_families {} n_processors 1 smooth TETRA_4 float PENTA_6 freeze TRI_3\n' \
            + 'ic_smooth_elements map all upto 0.4 iterations 5 prism_warp_weight 0.5 fix_families {} metric Quality n_processors 1 smooth TETRA_4 smooth TRI_3 float PENTA_6\n' \
            + 'ic_geo_set_modified 1\n' \
            + 'ic_delete_empty_parts\n'
        return text
    
    def gen_from_part(from_part: Part) -> str:
        '''生成从Part扩展到整个几何体创建四面体网格的Replay Script
        :param from_part: 从哪个Part开始创建网格
        :return: 生成的Replay Script'''
        # 从Part中读出所有的线和面
        obj_list = []
        for obj in from_part.objects:
            if type(obj) == Surface:
                obj_list.append('surface %s' % obj)
                for crv in obj.curves:
                    if str(crv) not in obj_list:
                        obj_list.append('curve %s' % crv)
            elif type(obj) == Curve and str(obj) not in obj_list:
                obj_list.append('curve %s' % crv)
        objs_expr = ' '.join(obj_list)

        # 先创建选中Part的网格
        parts_expr = 'GEOM SHEET ORFN ' + ' '.join([str(b) for b in Body.final_members] + [str(p) for p in Part.members])
        text = 'ic_undo_group_begin\n' \
             + 'ic_save_tetin temp_tetra.tin 0 0 {} {%s}\n' % objs_expr \
             + 'ic_run_tetra temp_tetra.tin ./tetra_mesh.uns input_geometry temp_tetra.tin run_cutter 1 delete_auto 1 run_smoother 0 fix_holes 1 n_processors 1 in_process 1 auto_vol 1 log ./tetra_cmd.log\n' \
             + 'ic_save_unstruct tetra_mesh0.uns\n' \
             + 'ic_unload_mesh\n' \
             + 'ic_load_unstruct tetra_mesh0.uns\n' \
             + 'ic_geo_set_modified 1\n' \
             + 'ic_save_unstruct tetra_mesh.uns\n' \
             + 'ic_uns_update_family_type visible {%s} {!LINE_2 TRI_3 !TETRA_4} update 0\n' % parts_expr \
             + 'ic_boco_solver\n' \
             + 'ic_uns_update_family_type visible {%s CREATED_MATERIAL_9} {!LINE_2 TRI_3 !TETRA_4} update 0\n' % parts_expr \
             + 'ic_boco_clear_icons\n' \
             + 'ic_geo_set_modified 1\n' \
             + 'ic_flood_fill no_repair\n' \
             + 'ic_uns_subset_create LeakLocation\n' \
             + 'ic_uns_subset_clear LeakLocation\n' \
             + 'ic_uns_diagnostic diag_type single disp_subset LeakLocation quiet 1\n' \
             + 'ic_uns_update_family_type visible {%s CREATED_MATERIAL_9} {!LINE_2 !TRI_3 !TETRA_4} update 0\n' % parts_expr \
             + 'ic_uns_subset_configure All -single_surfaces 1\n' \
             + 'ic_uns_subset_configure Selected -single_surfaces 1\n' \
             + 'ic_uns_subset_configure LeakLocation -single_surfaces 1\n' \
             + 'ic_uns_subset_visible LeakLocation 1\n' \
             + 'ic_uns_create_selection_subset 0\n' \
             + 'ic_uns_create_selection_edgelist 1\n' \
             + 'ic_uns_subset_configure uns_sel_0 -draw_nodes 1\n' \
             + 'ic_uns_subset_visible uns_sel_0 0\n' \
             + 'ic_uns_create_selection_edgelist 0\n' \
             + 'ic_uns_subset_delete uns_sel_0\n' \
             + 'ic_geo_delete_family CREATED_MATERIAL_9\n' \
             + 'ic_delete_empty_parts\n' \
             + 'ic_uns_subset_delete smooth_show_map\n' \
             + 'ic_uns_diag_reset_degen_min_max\n' \
             + 'ic_set_global geo_cad 0.07 toler\n' \
             + 'ic_undo_group_end\n\n'
        text += 'ic_uns_subset_configure All -shade flat_wire\n' \
             + 'ic_uns_subset_configure Selected -shade flat_wire\n' \
             + 'ic_uns_subset_configure LeakLocation -shade flat_wire\n' \
             + 'ic_uns_subset_visible LeakLocation 0\n' \
             + 'ic_uns_update_family_type visible {%s} {!LINE_2 !TRI_3 !TETRA_4} update 0\n' % parts_expr \
             + 'ic_uns_update_family_type visible {%s} {LINE_2 TRI_3 !TETRA_4} update 0\n' % parts_expr \
             + 'ic_set_global geo_cad 0.07 toler\n\n' \
        
        # 将选中Part的网格扩展到整个几何体
        text += 'ic_undo_group_begin\n' \
             + 'ic_save_tetin temp_tetra.tin\n' \
             + 'ic_save_unstruct temp_trimesh.uns 1 {} {} %s\n' % from_part \
             + 'ic_run_tetra temp_tetra.tin ./tetra_mesh.uns run_cutter 1 delete_auto 1 use_existing %s,temp_trimesh.uns run_smoother 0 fix_holes 1 n_processors 1 in_process 1 log ./tetra_cmd.log\n' % from_part \
             + 'ic_geo_set_modified 1\n' \
             + 'ic_uns_update_family_type visible {%s} {!LINE_2 TRI_3 !TETRA_4} update 0\n' % parts_expr \
             + 'ic_uns_subset_configure All -single_surfaces 1\n' \
             + 'ic_uns_subset_configure Selected -single_surfaces 1\n' \
             + 'ic_boco_solver\n' \
             + 'ic_boco_clear_icons\n' \
             + 'ic_uns_diagnostic diag_type single quiet 1\n' \
             + 'ic_smooth_elements map all upto 0.4 iterations 5 fix_families SHELL-WALLS n_processors 1 smooth TRI_3 float TETRA_4 laplace 1\n' \
             + 'ic_smooth_elements map all upto 0.4 iterations 5 prism_warp_weight 0.5 fix_families SHELL-WALLS n_processors 1 smooth TETRA_4 float PENTA_6 freeze TRI_3\n' \
             + 'ic_smooth_elements map all upto 0.4 iterations 5 prism_warp_weight 0.5 fix_families SHELL-WALLS metric Quality n_processors 1 smooth TETRA_4 smooth TRI_3 float PENTA_6\n' \
             + 'ic_geo_set_modified 1\n' \
             + 'ic_delete_empty_parts\n' \
             + 'ic_undo_group_end\n'
        return text
    

    def set_periodicity(offset_x: float = 0, offset_y: float = 0, offset_z: float = 0):
        '''设定网格的周期性
        :param offset_x: x方向偏移量
        :param offset_y: y方向偏移量
        :param offset_z: z方向偏移量'''
        TetraMesh.periodicity_script = 'ic_undo_group_begin\n' \
            + 'ic_coords_dir_into_global {%g %g %g} global\n' % (offset_x, offset_y, offset_z) \
            + 'ic_geo_set_periodic_data {axis {%g %g %g} type trans angle 0 base {0 0 0}}\n' % (offset_x, offset_y, offset_z) \
            + 'ic_undo_group_end\n'
        
    
    def replay() -> str:
        '''生成创建四面体网格的Replay Script
        :return: 生成的Replay Script'''
        return TetraMesh.periodicity_script + TetraMesh.script


def update_all(max_element_size: float = 1.0) -> str:
    '''输出全局网格设置及更新的Replay Script
    :param max_element_size: 全局网格最大尺寸
    :return: 生成的Replay Script'''
    # 全局网格设置（Global Mesh Setup）
    text = 'ic_set_meshing_params global 0 gref 1.0 gmax %g gfast 0 gedgec 0.2 gnat 0 gcgap 0 gnatref 10\n' % max_element_size
    # 局部网格细化设置
    for part in Part.members:
        if part.part_mesh_params is not None:
            text += update_part_mesh(part)
    # 更新Block（Update All）
    if len(Block.members) > 0:
        #parts_expr = 'GEOM ' + ' '.join([str(b) for b in Block.members] + [str(p) for p in Part.members])
        parts_expr = 'GEOM ' + ' '.join([str(b) for b in Body.final_members] + [str(p) for p in Part.members])
        text += 'ic_hex_set_mesh_params %s -version 110\n' % parts_expr
    return text


def update_part_mesh(part: Part) -> str:
    '''输出局部网格细化的Replay Script
    :param part: 要进行网格细化的part
    :return: 生成的Replay Script'''
    param = part.part_mesh_params
    text = 'ic_geo_set_family_params %s no_crv_inf ' % part.name \
         + 'prism %d emax %g ehgt %g hrat %g ' % (param.prism, param.emax, param.ehgt, param.hrat) \
         + 'nlay %d erat %g ewid %g emin %g ' % (param.nlay, param.erat, param.ewid, param.emin) \
         + 'edev %g prism_height_limit %g law %d ' % (param.edev, param.prism_height_limit, param.law) \
         + 'split_wall %d internal_wall %d\n' % (param.split_wall, param.internal_wall)
    return text


def gen_mesh_from_block(fName: str) -> str:
    '''输出由Block生成Mesh的Replay Script
    :param fName: 输出网格配置文件的文件名（不带扩展名）
    :return: 生成的Replay Script'''
    parts_expr = 'GEOM ' + ' '.join([str(b) for b in Body.final_members] + [str(p) for p in Part.members])
    text = 'ic_hex_write_file ./%s.uns %s proj 2 dim_to_mesh 3 no_boco\n' % (fName, parts_expr) \
            + 'ic_uns_load ./%s.uns 3 0 {} 1\n' % fName
    if Block.dimension == 2:
        text += 'ic_uns_update_family_type visible {%s} {!NODE !LINE_2 QUAD_4} update 0' % parts_expr
    else:
        text += 'ic_uns_update_family_type visible {%s} {!NODE !LINE_2 QUAD_4 !HEXA_8} update 0' % parts_expr
    return text


def replay_save_mesh(directory: str, fName: str, fluent_dir: str, scale: float = 1.0):
    '''输出保存Mesh至msh文件的Replay Script
    :param directory: 网格的工作路径
    :param fName: 输出网格msh文件的文件名（不带扩展名）
    :param fluent_dir: fluent应用程序的工作路径
    :param scale: 网格的比例尺
    :return: 生成的Replay Script'''
    text = 'ic_save_tetin %s.tin 0 0 {} {} 0 0 1\n' % fName
    if '%s.uns' % fName in os.listdir(directory):  #当uns文件已存在时，修改其文件名
        text += 'ic_rename %s.uns %s.uns.bak\n' % (fName, fName)
    text = text + 'ic_save_unstruct %s.uns 1 {} {} {}\n' % fName \
            + 'ic_hex_save_blocking %s.blk\n' % fName \
            + 'ic_boco_save %s.fbc\n' % fName \
            + 'ic_boco_save_atr %s.atr\n' % fName \
            + 'ic_save_project_file %s.prj\n' % fName
    if Block.dimension == 2:
        text += 'ic_exec {%s} -dom %s.uns -b %s.fbc -dim2d -scale %g,%g,%g ./%s.msh\n' % (
            fluent_dir, fName, fName, scale, scale, scale, fName)
    elif Block.dimension == 3:
        text += 'ic_exec {%s} -dom %s.uns -b %s.fbc -scale %g,%g,%g ./%s.msh\n' % (
            fluent_dir, fName, fName, scale, scale, scale, fName)
    return text